package gene

import (
	"errors"
	"playbook/ginsvr/src/global"
	"playbook/ginsvr/src/orm"
	"playbook/ginsvr/src/source/gene/tpl"
	"playbook/ginsvr/src/utils/fileutil"
	"strings"
	"text/template"

	"github.com/sirupsen/logrus"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

var tmpl *template.Template
var localpath orm.LocalPath
var err error
var funcMap template.FuncMap

type ParamItem struct {
	Type string
	Name string
}

type ParamCrtBean struct {
	Srcpath     string
	ParamBean   *orm.Parameter
	ParamSubLst *[]orm.Parameter
	ImpLst      []string
	Paramrelst  *[]orm.Paramrelation
	PrItemLst   *[]ParamItem
	Catagory    *orm.Catagory
}

type ScriptCrtSrc struct {
	Classpath   string
	ScriptName  string
	ProcessName string
	SrcName     string
	ParamIn     *orm.Parameter
	ParamOut    *orm.Parameter
	Catagory    *orm.Catagory
}

func init() {
	localpath = orm.GetLocalPath(global.USER)

	funcMap = template.FuncMap{
		"package":  GetPackfromClsp,
		"clsname":  GetClsNamefromClsp,
		"paramstr": GetParamStr,
		"title":    GetTitleStr,
		"lower":    strings.ToLower,
	}

	tmpl, err = template.New("ParamBean").Funcs(funcMap).Parse(tpl.Tplbean)
	tmpl, err = tmpl.New("Script").Funcs(funcMap).Parse(tpl.TplScript)
}

func GetPackfromClsp(classpath string) string {
	clslst := strings.Split(classpath, ".")
	packlst := clslst[:len(clslst)-1]
	return strings.Join(packlst, ".")
}

func GetClsNamefromClsp(classpath string) string {
	clslst := strings.Split(classpath, ".")
	return clslst[len(clslst)-1]
}

func GetTitleStr(str string) string {
	csr := cases.Title(language.English, cases.NoLower)
	return csr.String(str)
}

func getparamrelfromlst(paramid string, paramrelst *[]orm.Paramrelation) orm.Paramrelation {
	var paramreltmp orm.Paramrelation

	for _, paramrel := range *paramrelst {
		if paramid == paramrel.Subparamid {
			return paramrel
		}
	}

	return paramreltmp
}

func GetParamItmStu(param orm.Parameter, paramrelst *[]orm.Paramrelation, tableType string) ParamItem {
	var pi ParamItem

	csr := cases.Title(language.English)
	ptype := csr.String(param.Type)
	pclspath := csr.String(param.Classpath)

	if ptype == "Map" && pclspath != "Map" {
		paramrel := getparamrelfromlst(param.Parameterid, paramrelst)
		if paramrel.Relname != "" {
			pi.Type = param.Name
			pi.Name = paramrel.Relname
		}
	} else if ptype == "List" {
		paramrelst, _ := orm.GetParameterbyId(param.Parameterid, "")
		paramrel := paramrelst[0]
		if paramrel.Isbasetype == "0" {
			pi.Type = ptype + "<" + paramrel.Subparamid + ">"
		} else {
			paramBean, _, _ := orm.GetParamForCrtBean(paramrel.Subparamid, tableType)
			pi.Type = ptype + "<" + paramBean.Name + ">"
		}
		pi.Name = param.Name
	} else {
		pi.Type = param.Type
		pi.Name = param.Name
	}

	return pi
}

func GetParamStr(param orm.Parameter, paramrelst *[]orm.Paramrelation) string {
	var pcblstitem ParamCrtBean

	csr := cases.Title(language.English)
	ptype := csr.String(param.Type)
	pclspath := csr.String(param.Classpath)

	if ptype == "Map" && pclspath != "Map" {
		paramrel := getparamrelfromlst(param.Parameterid, paramrelst)
		if paramrel.Relname != "" {
			return param.Name + " " + paramrel.Relname
		}
	} else if ptype == "List" {
		paramrelst, _ := orm.GetParameterbyId(param.Parameterid, "")
		paramrel := paramrelst[0]
		if paramrel.Isbasetype == "0" {
			return ptype + "<" + paramrel.Subparamid + "> " + param.Name
		} else {
			pcblstitem.ParamBean, pcblstitem.ParamSubLst, pcblstitem.Paramrelst = orm.GetParamForCrtBean(paramrel.Subparamid, "0")
			return ptype + "<" + pcblstitem.ParamBean.Name + "> " + param.Name
		}
	}
	return param.Type + " " + param.Name
}

func getImpStrLst(pcb ParamCrtBean, tableType string) []string {
	var ImpLst []string
	var extMapTmp = make(map[string]string)

	for _, paramsub := range *pcb.ParamSubLst {
		if strings.Contains(paramsub.Classpath, ".") {
			ImpLst = append(ImpLst, paramsub.Classpath)
		} else if jit := orm.GetJavaImpText(paramsub.Type); jit.Javatype != "" {
			if _, ok := extMapTmp[jit.Javatype]; !ok {
				extMapTmp[jit.Javatype] = jit.Clspath
				ImpLst = append(ImpLst, jit.Clspath)
			}
		}

		if "LIST" == strings.ToUpper(paramsub.Type) {
			subparamrelLstDef, _ := orm.GetParameterbyId(paramsub.Parameterid, "")
			*pcb.Paramrelst = append(*pcb.Paramrelst, subparamrelLstDef[0])
		}
	}

	for _, parmrel := range *pcb.Paramrelst {
		if parmrel.Reltype == "lst" && parmrel.Isbasetype == "1" {
			subparmbean, _, _ := orm.GetParamForCrtBean(parmrel.Subparamid, tableType)
			if subparmbean.Classpath != "" {
				ImpLst = append(ImpLst, subparmbean.Classpath)
			}
		}
	}
	ImpLst = append(ImpLst, pcb.Catagory.Intfabsname)

	return ImpLst
}

func getScriptImpStrLst(data map[string]interface{}) []string {
	var ImpLst []string
	var extMapTmp = make(map[string]string)
	datasub := []interface{}{}
	if data["parameter"] != nil {
		datasub = data["parameter"].([]interface{})
	}

	for _, paramsub := range datasub {
		datasubitm := paramsub.(map[string]interface{})
		classpath := datasubitm["classPath"].(string)
		exttype := datasubitm["type"].(string)
		if strings.Contains(classpath, ".") {
			ImpLst = append(ImpLst, classpath)
		} else if jit := orm.GetJavaImpText(exttype); jit.Javatype != "" {
			if _, ok := extMapTmp[jit.Javatype]; !ok {
				extMapTmp[jit.Javatype] = jit.Clspath
				ImpLst = append(ImpLst, jit.Clspath)
			}
		}

		if strings.ToUpper(exttype) == "LIST" {
			dataLstItem := datasubitm["parameter"].(map[string]interface{})
			classlstitem := dataLstItem["classPath"].(string)
			if strings.Contains(classlstitem, ".") {
				ImpLst = append(ImpLst, classlstitem)
			}
		}
	}

	return ImpLst
}

func getProcessParamCrtBean(genetype string, processname string, paramid string, subpath string) ParamCrtBean {
	var pcb ParamCrtBean
	var prItemLst []ParamItem

	var catagoryid string
	if subpath == "" {
		catagoryid = "41a5b4085821b1fbf9053e10f6f00c27"
	} else {
		catagoryid = "d1f23befcbad5ab48dbad83b089ade75"
	}
	catagory := orm.GetCatagory("3", catagoryid)
	pcb.Catagory = &catagory

	pcb.PrItemLst = &prItemLst

	gsd := orm.GetGeneSrcDefine(genetype)
	if gsd.Srcpath == "" {
		return pcb
	}
	if subpath != "" {
		pcb.Srcpath = gsd.Srcpath + global.PTHSEP + processname + global.PTHSEP + subpath
	} else {
		pcb.Srcpath = gsd.Srcpath + global.PTHSEP + processname
	}

	pcb.ParamBean, pcb.ParamSubLst, pcb.Paramrelst = orm.GetParamForCrtBean(paramid, "0")
	//TODO 修改type
	pcb.ImpLst = getImpStrLst(pcb, "0")

	for _, param := range *pcb.ParamSubLst {
		prItemLst = append(prItemLst, GetParamItmStu(param, pcb.Paramrelst, "0"))
	}

	return pcb
}

func geneProcessParamBean(genetype string, processname string, paramid string, subpath string) {
	pcb := getProcessParamCrtBean(genetype, processname, paramid, subpath)

	file := fileutil.GetClearFile(pcb.Srcpath, pcb.ParamBean.Name+".java")
	if file == nil {
		return
	}
	defer file.Close()

	err := tmpl.ExecuteTemplate(file, "ParamBean", pcb)
	if err != nil {
		global.LOG.WithFields(logrus.Fields{
			"err": err,
		}).Error("gene src err.")
	}
}

func getScriptParamStu(data map[string]interface{}) []ParamItem {

	var paramLst []ParamItem

	datasub := []interface{}{}
	if data["parameter"] != nil {
		datasub = data["parameter"].([]interface{})
	}

	for _, paramsub := range datasub {
		datasubitm := paramsub.(map[string]interface{})
		classpath := datasubitm["classPath"].(string)
		exttype := datasubitm["type"].(string)

		var paramItem ParamItem

		if strings.ToUpper(exttype) == "MAP" && strings.Contains(classpath, ".") {
			paramItem.Name = datasubitm["name"].(string)
			paramItem.Type = GetClsNamefromClsp(classpath)
		} else if strings.ToUpper(exttype) == "LIST" {
			paramItem.Name = datasubitm["name"].(string)
			paramsubsub := datasubitm["parameter"].(map[string]interface{})
			classpathsub := paramsubsub["classPath"].(string)
			exttypesub := paramsubsub["type"].(string)
			if strings.ToUpper(exttypesub) == "MAP" {
				paramItem.Type = exttype + "<" + GetClsNamefromClsp(classpathsub) + ">"
			} else {
				paramItem.Type = exttype + "<" + exttypesub + ">"
			}
		} else {
			paramItem.Name = datasubitm["name"].(string)
			paramItem.Type = exttype
		}

		paramLst = append(paramLst, paramItem)
	}

	return paramLst
}

func getScriptForCrtBean(act orm.Activity) (ImpLst []string, ParamLst []ParamItem, beanname string) {
	var data interface{}

	tmp := act.Outputparameter["parameter"].(map[string]interface{})
	paramter := tmp["parameter"].([]interface{})

	for _, pstu := range paramter {
		pstumap := pstu.(map[string]interface{})
		if pstumap["name"].(string) == "data" {
			data = pstumap
			break
		}
	}

	ImpLst = getScriptImpStrLst(data.(map[string]interface{}))
	ParamLst = getScriptParamStu(data.(map[string]interface{}))
	beanname = GetClsNamefromClsp(data.(map[string]interface{})["classPath"].(string))

	return ImpLst, ParamLst, beanname
}

func getScriptParamCrtBean(genetype string, act orm.Activity) ParamCrtBean {
	var pcb ParamCrtBean
	var prItemLst []ParamItem
	var paramBean orm.Parameter

	catagory := orm.GetCatagory("3", "d1f23befcbad5ab48dbad83b089ade75")
	pcb.Catagory = &catagory

	pcb.PrItemLst = &prItemLst

	gsd := orm.GetGeneSrcDefine(genetype)
	if gsd.Srcpath == "" {
		return pcb
	}

	snlst := strings.Split(act.Name, ".")
	pcb.Srcpath = fileutil.SepTransform(gsd.Srcpath + global.PTHSEP + strings.TrimSpace(snlst[0]))

	ImpLst, ParamLst, _ := getScriptForCrtBean(act)
	pcb.ImpLst = ImpLst
	pcb.PrItemLst = &ParamLst
	pcb.ImpLst = append(pcb.ImpLst, pcb.Catagory.Intfabsname)

	_, after, _ := strings.Cut(pcb.Srcpath, "com")
	paramBean.Classpath = strings.Replace("com"+after, global.PTHSEP, ".", -1) + "." + snlst[1] + "Out"
	paramBean.Name = snlst[1] + "Out"
	pcb.ParamBean = &paramBean

	return pcb
}

func geneScriptParamBean(genetype string, act orm.Activity) ParamCrtBean {
	pcb := getScriptParamCrtBean(genetype, act)

	file := fileutil.GetClearFile(pcb.Srcpath, pcb.ParamBean.Name+".java")
	if file == nil {
		return pcb
	}
	defer file.Close()

	err := tmpl.ExecuteTemplate(file, "ParamBean", pcb)
	if err != nil {
		global.LOG.WithFields(logrus.Fields{
			"err": err,
		}).Error("gene script bean src err.")
	}

	return pcb
}

func GeneDictParamBean(genetype string, paramid string, path string) ParamCrtBean {
	pcb := getDictParamCrtBean(genetype, paramid, path)

	file := fileutil.GetClearFile(pcb.Srcpath, pcb.ParamBean.Name+".java")
	if file == nil {
		return pcb
	}
	defer file.Close()

	err := tmpl.ExecuteTemplate(file, "ParamBean", pcb)
	if err != nil {
		global.LOG.WithFields(logrus.Fields{
			"err": err,
		}).Error("gene dict bean src err.")
	}

	return pcb
}

func getDictParamCrtBean(genetype string, paramid string, path string) ParamCrtBean {
	var pcb ParamCrtBean
	var prItemLst []ParamItem

	catagory := orm.GetCatagory("3", "ad565b1b522ed1c607d2b77912cff07b")
	pcb.Catagory = &catagory

	pcb.PrItemLst = &prItemLst

	gsd := orm.GetGeneSrcDefine(genetype)
	if gsd.Srcpath == "" {
		return pcb
	}
	pcb.Srcpath = gsd.Srcpath + global.PTHSEP + path

	pcb.ParamBean, pcb.ParamSubLst, pcb.Paramrelst = orm.GetParamForCrtBean(paramid, "1")
	pcb.ImpLst = getImpStrLst(pcb, "1")

	for _, param := range *pcb.ParamSubLst {
		prItemLst = append(prItemLst, GetParamItmStu(param, pcb.Paramrelst, "1"))
	}

	return pcb
}

func geneScriptAtom(pcb ParamCrtBean, act orm.Activity) (ScriptCrtSrc, error) {
	var scs ScriptCrtSrc

	catagory := orm.GetCatagory("3", "05ba0285d6904c06933da755426f4246")
	scs.Catagory = &catagory

	snlst := strings.Split(act.Name, ".")
	_, after, _ := strings.Cut(pcb.Srcpath, "com")
	scs.Classpath = strings.Replace("com"+after, global.PTHSEP, ".", -1) + "." + snlst[1]

	scs.ProcessName = snlst[0]
	scs.ScriptName = snlst[1]

	scs.ParamIn = &orm.Parameter{
		Name:      "ScriptData",
		Type:      "Map",
		Classpath: "com.psbc.cbcbp.base.script.ScriptData",
	}

	scs.ParamOut = &orm.Parameter{
		Name:      pcb.ParamBean.Name,
		Type:      pcb.ParamBean.Type,
		Classpath: pcb.ParamBean.Classpath,
	}

	//if script atom exist，don't gene again
	scs.SrcName = pcb.Srcpath + global.PTHSEP + snlst[1] + ".java"
	if fileutil.FileExist(scs.SrcName, false) {
		return scs, nil
	}

	file := fileutil.GetClearFile(pcb.Srcpath, snlst[1]+".java")
	if file == nil {
		return scs, errors.New("gene Script Atom file fail!")
	}
	defer file.Close()

	err := tmpl.ExecuteTemplate(file, "Script", scs)
	if err != nil {
		global.LOG.WithFields(logrus.Fields{
			"err": err,
		}).Error("gene script bean src err.")
		return scs, errors.New("gene Script Atom source fail!")
	}

	return scs, nil
}

func GeneScriptStruct(genetype string, act orm.Activity) (ScriptCrtSrc, ParamCrtBean, error) {
	pcb := geneScriptParamBean(genetype, act)
	scs, err := geneScriptAtom(pcb, act)

	return scs, pcb, err
}

func GeneParamBean(genetype string, processname string, paramid string, subpath string) {
	if genetype == "composebean" || genetype == "subprocessbean" {
		geneProcessParamBean(genetype, processname, paramid, subpath)
	}

}
